﻿using System;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
using IBNet.UtilsDataStructuresAndExtensions;
using IBNet;

namespace IBNet.LongTermTrade
{
   public partial class LongTermTradeMainForm : Form, IWrapLongTermTradeMainForm
   {
      void client_ExecutionDetailsReceived(object sender, ExecutionDetailsEventArgs e)
      {
         if (_openingOrderIds.Contains(e.OrderId))
         // Then this execution detail is for an opening order that just opened 
         // a new position and now three bracket orders need to be submitted to 
         // bracket this new position.
         {
            Order openingOrder = _orderIdToOrderAndContract[e.OrderId].Order;

            SubmitBracketOrders(e);

            // Remove opening order order Id from _openingOrderIds so that
            // multiple partial fills of the opening order
            // do not cause multiple sets of bracket orders to be submitted.
            _openingOrderIds.Remove(e.OrderId);
         }
      }

      void SubmitBracketOrders(ExecutionDetailsEventArgs e)
      {
         Contract contract = e.Contract;
         Order openingOrder = _orderIdToOrderAndContract[e.OrderId].Order;

         DateTime orderExecTime = DateTime.ParseExact(e.Execution.Time,
            "yyyyMMdd  HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);
         DateTime orderExecTimePlusMaximumTradeLength = orderExecTime.AddDays(
            MaximumLengthOfTradeInDays);
         DateTime goodTillDate = new DateTime(orderExecTimePlusMaximumTradeLength.Year,
            orderExecTimePlusMaximumTradeLength.Month,
            orderExecTimePlusMaximumTradeLength.Day, 
            12, 59, 0);
         string goodTillDateString = goodTillDate.ToString("yyyyMMdd HH:mm:ss");

         int longTermTradeOcaNumber = LongTermTradeProgram.LongTermTradeOcaNumber++;
         string ocaGroup = contract.Symbol + "_LTT" + longTermTradeOcaNumber.ToString();

         Order targetOrder = new Order()
         {
            OrderType = OrderType.Limit,
            TotalQuantity = openingOrder.TotalQuantity,
            TimeInForce = TimeInForce.GoodTillCancel,
            GoodTillDate = goodTillDateString,
            OneCancelsAllGroup = ocaGroup
         };
         Order trailingStopOrder = new Order()
         {
            OrderType = OrderType.TrailingStop,
            TotalQuantity = openingOrder.TotalQuantity,
            TimeInForce = TimeInForce.GoodTillCancel,
            OneCancelsAllGroup = ocaGroup
         };
         Order timeStopOrder = new Order()
         {
            OrderType = OrderType.Market,
            TotalQuantity = openingOrder.TotalQuantity,
            TimeInForce = TimeInForce.GoodTillCancel,
            GoodAfterTime = goodTillDateString,
            OneCancelsAllGroup = ocaGroup
         };

         switch (openingOrder.ActionSide)
         {
         case ActionSide.Buy:
         {
            targetOrder.ActionSide = ActionSide.Sell;
            trailingStopOrder.ActionSide = ActionSide.Sell;
            timeStopOrder.ActionSide = ActionSide.Sell;
            targetOrder.LimitPrice =
               Math.Round(LongTargetPercentage * e.Execution.FillPrice, 2);
            trailingStopOrder.AuxPrice =
               Math.Round(TrailingPercentage * e.Execution.FillPrice, 2);
            break;
         }
         case ActionSide.Sell:
         {
            targetOrder.ActionSide = ActionSide.Buy;
            trailingStopOrder.ActionSide = ActionSide.Buy;
            timeStopOrder.ActionSide = ActionSide.Buy;
            targetOrder.LimitPrice =
               Math.Round(ShortTargetPercentage * e.Execution.FillPrice, 2);
            trailingStopOrder.AuxPrice =
               Math.Round(TrailingPercentage * e.Execution.FillPrice, 2);
            break;
         }
         };

         ClientWrappers.SubmitOrder(targetOrder, contract, OrderFlavor.Bracket);
         ClientWrappers.SubmitOrder(trailingStopOrder, contract, OrderFlavor.Bracket);
         ClientWrappers.SubmitOrder(timeStopOrder, contract, OrderFlavor.Bracket);
      }

      void client_PriceTickReceived(object sender, TickPriceEventArgs e)
      {
         switch (e.TickType)
         {
         case TickType.ClosePrice:
         {
            if (!_marketDataRequestIdToClosingPrice.ContainsKey(e.RequestId))
            {
               _marketDataRequestIdToClosingPrice.Add(e.RequestId, e.Price);
               _symbolFoundWhenRequestingClosingPrice = true;
               _closingPriceReceivedOrSymbolNotFound.Set();
            }
            string symbol = _requestIdForMarketDataToSymbol[e.RequestId];
            textBoxMarketData.WriteLine(
               symbol, " ", e.TickType.ToString(), " ", e.Price.ToString());
            break;
         }
         }
      }

      void client_NextValidOrderIdReceived(object sender, NextValidIdEventArgs e)
      {
         _currentOrderId = e.OrderId;
      }

      void client_ErrorOccurred(object sender, ErrorEventArgs e)
      {
         textBoxMessages.BeginInvoke((MethodInvoker)delegate
         {
            switch ((int)e.ErrorCode)
            {
            // Suppress server status messages
            case 2103:
            case 2104:
            case 2105:
            case 2106:
            case 2107:
            // Suppress 'not trading hours' warning
            case 399:
            // Suppress 'outside regular trading hours is ignored' warning
            case 2109:
            {
               return;
            }
            // Duplicate order Id.
            case 103:
            {
               int orderId = e.Id;
               string symbol = _orderIdToOrderAndContract[orderId].Contract.Symbol;
               textBoxMessages.WriteLine(symbol, " duplicate order id ", orderId.ToString());
               return;
            }
            // Symbol not found.
            case 200:
            {
               _symbolFoundWhenRequestingClosingPrice = false;
               _closingPriceReceivedOrSymbolNotFound.Set();
               break;
            }
            // Order cancelled.
            case 202:
            {
               int orderId = e.Id;
               OrderAndContract orderAndContract;

               if (_orderIdToOrderAndContract.TryGetValue(orderId,
                  out orderAndContract))
               {
                  Order canceledOrder = orderAndContract.Order;
                  string canceledSymbol = orderAndContract.Contract.Symbol;

                  textBoxMessages.WriteLine(canceledSymbol,
                     " order cancelled. OrderId = ",
                     orderId.ToString(), ". Reason: ", e.ErrorMessage);
               }
               break;
            }
            // Unable to short _symbol.
            case 404:
            {
               int orderId = e.Id;
               OrderAndContract orderAndContract = _orderIdToOrderAndContract[orderId];
               textBoxMessages.WriteLine("Unable to short symbol " + orderAndContract.Contract.Symbol);
               client.CancelOrder(orderId);
               _orderIdToOrderAndContract.Remove(orderId);
               break;
            }
            default:
            {
               textBoxMessages.AppendText("Error ");
               textBoxMessages.AppendText(e.ErrorCode.ToString());
               textBoxMessages.AppendText(" ");
               textBoxMessages.AppendText(e.ErrorMessage);
               textBoxMessages.AppendText(". Id = ");
               textBoxMessages.AppendText(e.Id.ToString());
               textBoxMessages.AppendText("\r\n");
               break;
            }
            }
         });
      }

      //void WriteToErrors(params string[] messageArray)
      //{
      //   textBoxMessages.InvokeIfNeeded((Action)delegate
      //   {
      //      foreach (string message in messageArray)
      //      {
      //         textBoxMessages.AppendText(message);
      //      }
      //      textBoxMessages.AppendText("\r\n");
      //   }, InvocationMethod.BeginInvoke);
      //}

      //void WriteToErrors(string errorMessage)
      //{
      //   textBoxMessages.InvokeIfNeeded((Action)delegate
      //   {
      //      textBoxMessages.AppendText(errorMessage);
      //      textBoxMessages.AppendText("\r\n");
      //   }, InvocationMethod.BeginInvoke);
      //}
   }
}
